﻿namespace TomatoBreak.UnitSpecs.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Web.Mvc;

    using Machine.Specifications;
    using NSubstitute;

    using DataAccess;
    using TomatoBreak.DomainObjects;
    using ExtensionMethods;
    using Infrastructure;
    using Web;

    public abstract class SessionControllerSpec
    {
        protected static IRepository<User> repository;
        protected static IFormsAuthentication formsAuthentication;
        protected static SessionController controller;

        Establish context = () =>
        {
            repository = Substitute.For<IRepository<User>>();
            formsAuthentication = Substitute.For<IFormsAuthentication>();

            controller = new SessionController(repository, formsAuthentication);
        };
    }

    [Subject(typeof(SessionController))]
    public class when_creating_session_for_valid_user : SessionControllerSpec
    {
        const string UserName = "KaziManzurRashid";

        static HttpStatusCodeResult result;

        Establish context = () =>
        {
            var user = Substitute.For<User>();

            user.UserName.Returns(UserName);
            user.Locked.Returns(false);

            user.HasPassword(Arg.Any<string>()).Returns(true);

            repository.FindOne(Arg.Any<Expression<Func<User, bool>>>()).Returns(user);
        };

        Because of = () => result = (HttpStatusCodeResult)controller.Create(new CreateSessionModel { EmailOrUserName = UserName, Password = "foobar" });

        It should_sign_in_user = () => formsAuthentication.Received().SignIn(UserName, false);

        It should_return_http_created = () => result.StatusCode.ShouldEqual(201);
    }

    [Subject(typeof(SessionController))]
    public class when_creating_session_with_invalid_model_state : SessionControllerSpec
    {
        static JsonResult result;

        Establish context = () =>
        {
            controller.ModelState.AddModelError("UserName", "User name cannot be blank");
            controller.ModelState.AddModelError("Password", "Password cannot be blank");
        };

        Because of = () => result = (JsonResult)controller.Create(new CreateSessionModel());

        It should_return_model_state_errors_in_json = () => result.Data.Property("errors").As<IEnumerable<object>>().Count().ShouldEqual(2);
    }

    [Subject(typeof(SessionController))]
    public class when_creating_session_for_non_existent_user : SessionControllerSpec
    {
        static JsonResult result;

        Because of = () => result = (JsonResult)controller.Create(new CreateSessionModel());

        It should_return_errors_in_json = () => result.Data.Property("errors").As<IEnumerable<object>>().ShouldNotBeEmpty();
    }

    [Subject(typeof(SessionController))]
    public class when_creating_session_for_locked_user : SessionControllerSpec
    {
        static JsonResult result;

        Establish context = () =>
        {
            var user = ObjectMother.CreateUser();
            user.Lock();

            repository.FindOne(Arg.Any<Expression<Func<User, bool>>>()).Returns(user);
        };

        Because of = () => result = (JsonResult)controller.Create(new CreateSessionModel());

        It should_return_errors_in_json = () => result.Data.Property("errors").As<IEnumerable<object>>().ShouldNotBeEmpty();
    }

    [Subject(typeof(SessionController))]
    public class when_creating_session_with_non_matching_passwor : SessionControllerSpec
    {
        static JsonResult result;

        Establish context = () => repository.FindOne(Arg.Any<Expression<Func<User, bool>>>()).Returns(ObjectMother.CreateUser());

        Because of = () => result = (JsonResult)controller.Create(new CreateSessionModel { EmailOrUserName = "dummy", Password = "foobar" });

        It should_return_errors_in_json = () => result.Data.Property("errors").As<IEnumerable<object>>().ShouldNotBeEmpty();
    }

    [Subject(typeof(SessionController))]
    public class when_destroying_session : SessionControllerSpec
    {
        static HttpStatusCodeResult result;

        Because of = () => result = (HttpStatusCodeResult)controller.Destroy();

        It should_signout_user = () => formsAuthentication.Received().SignOut();

        It should_return_http_status_code_ok = () => result.StatusCode.ShouldEqual(200);
    }
}